// boost
#include <boost/filesystem.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/format.hpp>

// ravi
#include "compiler.hpp"
#include "util.hpp"

// seq
#include "ConfigLoader.h"

#include "filesystem_helper.hpp"
#include "io_util.hpp"

using namespace std;
using namespace boost;

namespace ravi {

ConfigLoader::ConfigLoader() {
    clearLoadPath();
}

vector<filesystem::path> ConfigLoader::loadPath(const string& scriptCategoryName) const {
    LoadPathMap::const_iterator i = loadPathMap_.find(scriptCategoryName), e = loadPathMap_.end();
    if (i==e) {
        throw script_error(io::str(format("Ilegal script[%1%].")%scriptCategoryName));
    }
    return i->second;
}

void ConfigLoader::addLoadPath(const string& scriptCategoryName, const filesystem::path& filepath) {
    using namespace boost::filesystem;
    LoadPathMap::iterator i = loadPathMap_.find(scriptCategoryName), e = loadPathMap_.end();
    if (i==e) {
        throw script_error(io::str(format("Ilegal script[%1%].")%scriptCategoryName));
    }
    if (find(i->second.begin(), i->second.end(), filepath) == i->second.end()) {
        loadPathMap_[scriptCategoryName].push_back(filepath);
    }
}

void ConfigLoader::setLoadPathes(const std::string& scriptCategoryName, const std::vector<boost::filesystem::path>& filepathes) {
    LoadPathMap::iterator i = loadPathMap_.find(scriptCategoryName), e = loadPathMap_.end();
    if (i != e) {
        i->second = filepathes;
    }
}

void ConfigLoader::clearLoadPath() {
    loadPathMap_.clear();
    loadPathMap_.insert(make_pair("event",     vector<filesystem::path>()));
    loadPathMap_.insert(make_pair("vstplugin", vector<filesystem::path>()));
    loadPathMap_.insert(make_pair("setup",     vector<filesystem::path>()));
    loadPathMap_.insert(make_pair("script",    vector<filesystem::path>()));
    loadPathMap_.insert(make_pair("color",     vector<filesystem::path>()));
    loadPathMap_.insert(make_pair("doc",       vector<filesystem::path>()));
    loadPathMap_.insert(make_pair("editor",    vector<filesystem::path>()));
}

bool ConfigLoader::loadConfig(const string& scriptCategoryName, const string& scriptName) {
    if (       scriptCategoryName == "event"
            || scriptCategoryName == "editor"
            || scriptCategoryName == "setup") {
        string scriptNameLowCase(tolower(scriptName));
        if (g_compiler.is_loaded(scriptNameLowCase)) return true;
        filesystem::path scriptFileName(filesystem::change_extension(scriptNameLowCase, "rav"));
        LoadPathMap::iterator i = loadPathMap_.find(scriptCategoryName), e = loadPathMap_.end();
        if (i==e) return false;
        LoadPathMapVal::iterator i2 = i->second.begin(), e2 = i->second.end();
        if (i2==e2) return false;
        for (; i2 != e2; ++i2) {
            filesystem::path fpath(*i2 / scriptFileName);
            if (is_readble_file(fpath)) {
                g_compiler.compile_file(fpath);
                return true;
            }
        }
    } else if (scriptCategoryName == "vstplugin") {
        // TODO:
    } else if (scriptCategoryName == "doc") {
        // TODO:
    }
    return false;
}

bool ConfigLoader::loadConfigAll(const std::string& scriptCategoryName) {
    using namespace boost::filesystem;
    if (!(     scriptCategoryName == "event"
            || scriptCategoryName == "editor"
            || scriptCategoryName == "setup")
            ) {
        return false;
    }
    LoadPathMap::iterator i = loadPathMap_.find(scriptCategoryName), e = loadPathMap_.end();
    if (i==e) return false;
    LoadPathMapVal::iterator i2 = i->second.begin(), e2 = i->second.end();
    if (i2==e2) return false;
    for (; i2 != e2; ++i2) {
        if (!is_directory(*i2)) continue;
        recursive_directory_iterator di(*i2);
        recursive_directory_iterator de;
        for (; di != de; ++di) {
            string ext(extension(*di));
            if (ext != di->leaf() && ext == ".rav") {
                if (is_readble_file(*di)) {
                    g_compiler.compile_file(*di);
                }
            }
        }
    }
    return true;
}

bool ConfigLoader::isValidCategory(const std::string& scriptCategoryName) {
    return loadPathMap_.find(scriptCategoryName) != loadPathMap_.end();
}


ConfigLoader g_config_loader;

} // end of namespace ravi

